`timescale 1ns / 1ps

`include "data_width.vh"

// VERTEX_PIPE_NUM 条写回流水线整合为 VERTEX_PIPE_NUM * VERTEX_BRAM_DWIDTH 位传递
// 为了代码通用性，32 位点编号使用 4 个 channel 传递，与bfs值同步传递
// start fifo when wr_start is assert
// stop and reset fifo when wr_end is assert
module final_write_back_vertex #(parameter
    DST_ID_DWIDTH               = `DST_ID_DWIDTH,
    VERTEX_BRAM_DWIDTH          = `VERTEX_BRAM_DWIDTH,
    WB_VALID_WIDTH              = `WB_VALID_WIDTH,
    VERTEX_PIPE_NUM             = `VERTEX_PIPE_NUM,

    VERTEX_DATA_CHANNEL_WIDTH   = `VERTEX_DATA_CHANNEL_WIDTH,
    VERTEX_ID_CHANNEL_WIDTH     = `VERTEX_ID_CHANNEL_WIDTH
) (
    input                                                   clk,
    input                                                   front_rst,
    output reg                                              rst,

    input                                                   wr_start,
    input                                                   wr_end,
    // M16
    input [WB_VALID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]        wb_dst_data_valid,
    input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         wb_dst_addr,
    input [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]    wb_dst_data,

    output                                                  m_Vertex_Data_axis_tvalid,
    input                                                   m_Vertex_Data_axis_tready,
    output [VERTEX_DATA_CHANNEL_WIDTH - 1 : 0]              m_Vertex_Data_axis_tdata,
    output                                                  m_Vertex_Data_axis_tlast,

    output                                                  m_Vertex_Id_axis_tvalid_1,
    input                                                   m_Vertex_Id_axis_tready_1,
    output [VERTEX_ID_CHANNEL_WIDTH - 1 : 0]                m_Vertex_Id_axis_tdata_1,
    output                                                  m_Vertex_Id_axis_tlast_1,

    output                                                  m_Vertex_Id_axis_tvalid_2,
    input                                                   m_Vertex_Id_axis_tready_2,
    output [VERTEX_ID_CHANNEL_WIDTH - 1 : 0]                m_Vertex_Id_axis_tdata_2,
    output                                                  m_Vertex_Id_axis_tlast_2,

    output                                                  m_Vertex_Id_axis_tvalid_3,
    input                                                   m_Vertex_Id_axis_tready_3,
    output [VERTEX_ID_CHANNEL_WIDTH - 1 : 0]                m_Vertex_Id_axis_tdata_3,
    output                                                  m_Vertex_Id_axis_tlast_3,

    output                                                  m_Vertex_Id_axis_tvalid_4,
    input                                                   m_Vertex_Id_axis_tready_4,
    output [VERTEX_ID_CHANNEL_WIDTH - 1 : 0]                m_Vertex_Id_axis_tdata_4,
    output                                                  m_Vertex_Id_axis_tlast_4);

    // VERTEX_BRAM_DWIDTH bit fifo for buffer result
    wire [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0] fifo_d_out;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_d_out_valid;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_d_empty;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_d_full;
    wire                                                fifo_d_read_next;
    // VERTEX_BRAM_DWIDTH bit fifo for buffer id
    wire [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0] fifo_id_out_1;
    wire [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0] fifo_id_out_2;
    wire [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0] fifo_id_out_3;
    wire [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0] fifo_id_out_4;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_out_valid_1;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_out_valid_2;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_out_valid_3;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_out_valid_4;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_empty_1;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_empty_2;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_empty_3;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_empty_4;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_full_1;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_full_2;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_full_3;
    wire [VERTEX_PIPE_NUM - 1 : 0]                      fifo_id_full_4;
    wire                                                fifo_id_read_next_1;
    wire                                                fifo_id_read_next_2;
    wire                                                fifo_id_read_next_3;
    wire                                                fifo_id_read_next_4;

    // support of status
    wire                                        fifo_2_d_empty;
    wire                                        fifo_2_d_full;
    wire                                        fifo_2_id_empty_1;
    wire                                        fifo_2_id_empty_2;
    wire                                        fifo_2_id_empty_3;
    wire                                        fifo_2_id_empty_4;
    wire                                        fifo_2_id_full_1;
    wire                                        fifo_2_id_full_2;
    wire                                        fifo_2_id_full_3;
    wire                                        fifo_2_id_full_4;

    assign fifo_d_read_next     = !(|fifo_d_empty) && !fifo_2_d_full;
    assign fifo_id_read_next_1  = !(|fifo_id_empty_1) && !fifo_2_id_full_1;
    assign fifo_id_read_next_2  = !(|fifo_id_empty_2) && !fifo_2_id_full_2;
    assign fifo_id_read_next_3  = !(|fifo_id_empty_3) && !fifo_2_id_full_3;
    assign fifo_id_read_next_4  = !(|fifo_id_empty_4) && !fifo_2_id_full_4;

    always @ (posedge clk) begin
        rst <= front_rst;
    end

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M17_BLOCK_1
            // for result
            final_wb_dst_data_8bit_fifo Final_Wb_Dst_Data_8bit_Fifo (
                .clk(clk), .srst(front_rst),
                .din(wb_dst_data[VERTEX_BRAM_DWIDTH * (i + 1) - 1 : VERTEX_BRAM_DWIDTH * i]),
                .wr_en(|wb_dst_data_valid[WB_VALID_WIDTH * (i + 1) - 1 : WB_VALID_WIDTH * i] && wr_start),
                .rd_en(fifo_d_read_next),

                .dout(fifo_d_out[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]), .valid(fifo_d_out_valid[i]),
                .empty(fifo_d_empty[i]), .prog_full(fifo_d_full[i]));
            // for vertex id
            final_wb_dst_data_8bit_fifo Final_Wb_Dst_Id_8bit_Fifo_1 (
                .clk(clk), .srst(front_rst),
                .din(wb_dst_addr[i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .wr_en(|wb_dst_data_valid[WB_VALID_WIDTH * (i + 1) - 1 : WB_VALID_WIDTH * i] && wr_start),
                .rd_en(fifo_id_read_next_1),

                .dout(fifo_id_out_1[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]), .valid(fifo_id_out_valid_1[i]),
                .empty(fifo_id_empty_1[i]), .prog_full(fifo_id_full_1[i]));
            final_wb_dst_data_8bit_fifo Final_Wb_Dst_Id_8bit_Fifo_2 (
                .clk(clk), .srst(front_rst),
                .din(wb_dst_addr[i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH * 2 - 1 : i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH]),
                .wr_en(|wb_dst_data_valid[WB_VALID_WIDTH * (i + 1) - 1 : WB_VALID_WIDTH * i] && wr_start),
                .rd_en(fifo_id_read_next_2),

                .dout(fifo_id_out_2[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]), .valid(fifo_id_out_valid_2[i]),
                .empty(fifo_id_empty_2[i]), .prog_full(fifo_id_full_2[i]));
            final_wb_dst_data_8bit_fifo Final_Wb_Dst_Id_8bit_Fifo_3 (
                .clk(clk), .srst(front_rst),
                .din(wb_dst_addr[i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH * 3 - 1 : i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH * 2]),
                .wr_en(|wb_dst_data_valid[WB_VALID_WIDTH * (i + 1) - 1 : WB_VALID_WIDTH * i] && wr_start),
                .rd_en(fifo_id_read_next_3),

                .dout(fifo_id_out_3[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]), .valid(fifo_id_out_valid_3[i]),
                .empty(fifo_id_empty_3[i]), .prog_full(fifo_id_full_3[i]));
            final_wb_dst_data_8bit_fifo Final_Wb_Dst_Id_8bit_Fifo_4 (
                .clk(clk), .srst(front_rst),
                .din(wb_dst_addr[i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH * 4 - 1 : i * DST_ID_DWIDTH + VERTEX_BRAM_DWIDTH * 3]),
                .wr_en(|wb_dst_data_valid[WB_VALID_WIDTH * (i + 1) - 1 : WB_VALID_WIDTH * i] && wr_start),
                .rd_en(fifo_id_read_next_4),

                .dout(fifo_id_out_4[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]), .valid(fifo_id_out_valid_4[i]),
                .empty(fifo_id_empty_4[i]), .prog_full(fifo_id_full_4[i]));
        end
    endgenerate
    
    final_wb_dst_data_128bit_fifo Final_Wb_Dst_Data_128bit_Fifo (
        .clk(clk), .srst(front_rst),
        .din(fifo_d_out), .wr_en(fifo_d_out_valid[0]), .rd_en(m_Vertex_Data_axis_tready),

        .dout(m_Vertex_Data_axis_tdata), .valid(m_Vertex_Data_axis_tvalid),
        .empty(fifo_2_d_empty), .prog_full(fifo_2_d_full));
    
    final_wb_dst_data_128bit_fifo Final_Wb_Dst_Id_128bit_Fifo_1 (
        .clk(clk), .srst(front_rst),
        .din(fifo_id_out_1), .wr_en(fifo_id_out_valid_1[0]), .rd_en(m_Vertex_Id_axis_tready_1),

        .dout(m_Vertex_Id_axis_tdata_1), .valid(m_Vertex_Id_axis_tvalid_1),
        .empty(fifo_2_id_empty_1), .prog_full(fifo_2_id_full_1));

    final_wb_dst_data_128bit_fifo Final_Wb_Dst_Id_128bit_Fifo_2 (
        .clk(clk), .srst(front_rst),
        .din(fifo_id_out_2), .wr_en(fifo_id_out_valid_2[0]), .rd_en(m_Vertex_Id_axis_tready_2),

        .dout(m_Vertex_Id_axis_tdata_2), .valid(m_Vertex_Id_axis_tvalid_2),
        .empty(fifo_2_id_empty_2), .prog_full(fifo_2_id_full_2));
    
    final_wb_dst_data_128bit_fifo Final_Wb_Dst_Id_128bit_Fifo_3 (
        .clk(clk), .srst(front_rst),
        .din(fifo_id_out_3), .wr_en(fifo_id_out_valid_3[0]), .rd_en(m_Vertex_Id_axis_tready_3),

        .dout(m_Vertex_Id_axis_tdata_3), .valid(m_Vertex_Id_axis_tvalid_3),
        .empty(fifo_2_id_empty_3), .prog_full(fifo_2_id_full_3));

    final_wb_dst_data_128bit_fifo Final_Wb_Dst_Id_128bit_Fifo_4 (
        .clk(clk), .srst(front_rst),
        .din(fifo_id_out_4), .wr_en(fifo_id_out_valid_4[0]), .rd_en(m_Vertex_Id_axis_tready_4),

        .dout(m_Vertex_Id_axis_tdata_4), .valid(m_Vertex_Id_axis_tvalid_4),
        .empty(fifo_2_id_empty_4), .prog_full(fifo_2_id_full_4));

endmodule